package com.open.auth.config;

import java.security.KeyPair;

import javax.sql.DataSource;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JdbcTokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import org.springframework.security.oauth2.provider.token.store.KeyStoreKeyFactory;

import com.open.auth.service.impl.ClientDetailsServiceImpl;
import com.open.auth.service.impl.UserDetailsServiceImpl;

/**
 * 授权服务配置
 */
@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {

	@Autowired
	private PasswordEncoder passwordEncoder;
	
	@Autowired
	private ClientDetailsServiceImpl clientDetailsService;
	
	@Autowired
	private DataSource dataSource;
	
	@Autowired
	private RedisConnectionFactory redisConnectionFactory;
	
	@Autowired
    private UserDetailsServiceImpl userDetailsService;
	
	@Autowired
	private AuthenticationManager authenticationManager;
	
	/**
	 * 签名key
	 */
	private String signingKey = "jwt_123qbc";
	
	/**
	 * 配置客户端管理
	 */
	@Override
	public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
		//设置密码编码
		clientDetailsService.setPasswordEncoder(passwordEncoder);
		//读取客户端配置
		clients.withClientDetails(clientDetailsService);
	}

    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        /** 设置令牌存储 */
        endpoints.tokenStore(tokenStore())
        	.userDetailsService(userDetailsService)
        	.accessTokenConverter(jwtAccessTokenConverter())
        	.authenticationManager(authenticationManager);
        
        //endpoints.tokenServices(defaultTokenServices());
        /** 最后一个参数为替换之后授权页面的url */
        endpoints.pathMapping("/oauth/confirm_access", "/custom/oauth/confirm_access");
    }
	
    /**
	 * 令牌存储，基于JWT
	 * 令牌可以保存在内存中；也可以保存在数据库、Redis中
	 * 
	 * @return
	 */
	@Bean
	public TokenStore tokenStore() {
		/** 基于 JDBC 实现，令牌保存到数据 */
		 return new JdbcTokenStore(dataSource);
		// return InMemoryTokenStore();
//		 return new RedisTokenStore(redisConnectionFactory);
//		return new CustomRedisTokenStore(redisConnectionFactory);
		// return new JwtTokenStore(jwtAccessTokenConverter());
	}
	
	/**
	 * JWT令牌校验工具
	 * 
	 * @return
	 */
	@Bean
	public JwtAccessTokenConverter jwtAccessTokenConverter() {
		JwtAccessTokenConverter jwtAccessTokenConverter = new JwtAccessTokenConverter();
		// 设置JWT签名密钥。它可以是简单的MAC密钥，也可以是RSA密钥
		jwtAccessTokenConverter.setSigningKey(signingKey);
//		jwtAccessTokenConverter.setKeyPair(keyPair());
		return jwtAccessTokenConverter;
	}
	
	@Bean
	public KeyPair keyPair() {
		KeyPair keyPair = new KeyStoreKeyFactory(
            new ClassPathResource("keystore.jks"), "foobar".toCharArray())
            .getKeyPair("test");
        return keyPair;
	}
	
	/**
	 * 第三步：端点安全约束
	 * 
	 * 配置令牌安全约束
	 */
	@Override
	public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
	    security
	        //对应/oauth/token_key 公开，获取公钥需要访问该端点
	        .tokenKeyAccess("permitAll()")
	        //对应/oauth/check_token ，路径公开，校验Token需要请求该端点
	        .checkTokenAccess("permitAll()")
	        //允许客户端进行表单身份验证,使用表单认证申请令牌
	        .allowFormAuthenticationForClients();
	}
}
